package net.zoneland.zrdp.common.utils.file;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import javax.servlet.http.HttpServletResponse;

import net.zoneland.zrdp.common.utils.DateUtil;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import net.zoneland.zrdp.common.config.ZonevueConfig;
import net.zoneland.zrdp.common.utils.ServletUtils;
import net.zoneland.zrdp.common.utils.uuid.IdUtils;


/**
 * 文件处理工具类
 *
 * @author zonevue
 */
public class FileUtils {
    public static final String FILENAME_PATTERN = "[a-zA-Z0-9_\\-|.\\u4e00-\\u9fa5]+";

    /**
     * 默认下载的地址
     */
    private static final String DEFAULT_DOWNLOAD_PATH = ZonevueConfig.getDownloadPath();
    private FileUtils() {
        throw new IllegalStateException("Utility class");
    }
    /**
     * 输出指定文件的byte数组
     *
     * @param filePath 文件路径
     * @param os 输出流(这里不关闭输出流，哪儿创建哪儿关闭)
     */
    public static void writeBytes(final String filePath, final OutputStream os) throws IOException {
        final Path basePath = Paths.get(DEFAULT_DOWNLOAD_PATH);
        // normalize()方法会移除路径中的..和.等相对路径部分，以防止目录遍历
        final Path normalizedPath = basePath.resolve(filePath).normalize();
        // 检查规范化后的路径是否以安全的基础路径开头
        if (!normalizedPath.startsWith(basePath)) {
            throw new FileNotFoundException(filePath);
        }
        final File file = new File(filePath);
        if (!file.exists()) {
            throw new FileNotFoundException(filePath);
        }
        try (final InputStream fis = Files.newInputStream(file.toPath())) {
            final byte[] b = new byte[1024];
            int length;
            while ((length = fis.read(b)) > 0) {
                os.write(b, 0, length);
            }
        } catch (final IOException e) {
            throw new IOException("打开文件失败：" + filePath, e);
        }
    }

    /**
     * 写数据到文件中
     *
     * @param data 数据
     * @return 目标文件
     * @throws IOException IO异常
     */
    public static String writeImportBytes(final byte[] data) throws IOException {
        return writeBytes(data, ZonevueConfig.getImportPath());
    }

    /**
     * 写数据到文件中
     *
     * @param data 数据
     * @param uploadDir 目标文件
     * @return 目标文件
     * @throws IOException IO异常
     */
    public static String writeBytes(final byte[] data, final String uploadDir) throws IOException {
        final String extension = getFileExtendName(data);
        final String pathName = String.format("%s/%s.%s", DateUtil.datePath(), IdUtils.fastUUID(), extension);
        final File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName);
        try (final OutputStream fos = Files.newOutputStream(file.toPath())) {
            fos.write(data);
        }

        return FileUploadUtils.getPathFileName(uploadDir, pathName);
    }

    /**
     * 删除文件
     *
     * @param filePath 文件
     * @return 是否删除成功
     */
    public static boolean deleteFile(final String filePath) {
        final Path basePath = Paths.get(DEFAULT_DOWNLOAD_PATH);
        // normalize()方法会移除路径中的..和.等相对路径部分，以防止目录遍历
        final Path normalizedPath = basePath.resolve(filePath).normalize();
        // 检查规范化后的路径是否以安全的基础路径开头
        if (normalizedPath.startsWith(basePath)) {
            final File file = normalizedPath.toFile();
            // 路径为文件且不为空则进行删除
            if (file.isFile() && file.exists()) {
                return file.delete();
            }
        }
        return false;
    }

    /**
     * 文件名称验证
     *
     * @param filename 文件名称
     * @return true 正常 false 非法
     */
    public static boolean isValidFilename(final String filename)
    {
        return filename.matches(FILENAME_PATTERN);
    }

    /**
     * 检查文件是否可下载
     *
     * @param resource 需要下载的文件
     * @return true 正常 false 非法
     */
    public static boolean checkAllowDownload(final String resource) {
        // 禁止目录上跳级别
        if (StringUtils.contains(resource, "..")) {
            return false;
        }

        // 检查允许下载的文件规则
        return ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource));
    }

    /**
     * 下载文件名重新编码
     *
     * @param response 响应对象
     * @param realFileName 真实文件名
     */
    public static void setAttachmentResponseHeader(final HttpServletResponse response, final String realFileName) {
        final String percentEncodedFileName = ServletUtils.urlEncode(realFileName);

        final String contentDispositionValue = "attachment; filename=" +
                percentEncodedFileName +
                ";" +
                "filename*=" +
                "utf-8''" +
                percentEncodedFileName;

        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
        response.setHeader("Content-disposition", contentDispositionValue);
        response.setHeader("download-filename", percentEncodedFileName);
    }


    /**
     * 获取图像后缀
     *
     * @param photoByte 图像数据
     * @return 后缀名
     */
    public static String getFileExtendName(final byte[] photoByte) {
        if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
                && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) {
            return "gif";
        } else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) {
            return "jpg";
        } else if ((photoByte[0] == 66) && (photoByte[1] == 77)) {
            return "bmp";
        } else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) {
            return "png";
        }
        return "jpg";
    }

    /**
     * 获取文件名称 /profile/upload/2022/04/16/zonevue.png -- zonevue.png
     *
     * @param fileName 路径名称
     * @return 没有文件路径的名称
     */
    public static String getName(final String fileName) {
        if (fileName == null) {
            return null;
        }
        final int lastUnixPos = fileName.lastIndexOf('/');
        final int lastWindowsPos = fileName.lastIndexOf('\\');
        final int index = Math.max(lastUnixPos, lastWindowsPos);
        return fileName.substring(index + 1);
    }

    /**
     * 获取不带后缀文件名称 /profile/upload/2022/04/16/zonevue.png -- zonevue
     *
     * @param fileName 路径名称
     * @return 没有文件路径和后缀的名称
     */
    public static String getNameNotSuffix(final String fileName) {
        if (fileName == null) {
            return null;
        }
        return FilenameUtils.getBaseName(fileName);
    }
}
